#include <v8.h>
#include <syslog.h>
#include <fcgi_stdio.h>

#include "counterpartExceptions.h"

using namespace v8;
using namespace std;

enum HeaderState
{
	hsNone,
	hsFirstCR,
	hsFirstLF,
	hsSecondCR,
	hsSecondLF,
	hsNoMoreHeaders = hsSecondLF
};

static HeaderState sHeaderState; // hsNone at the start of each request because of the way Counterpart forks

static void MaintainHeaderState (const char * buffer, int length)
{
	int index = 0;

	while (index < length)
	{
		switch (buffer [index])
		{
			default :

				sHeaderState = hsNone;
				break;

			case '\r' :

				switch (sHeaderState)
				{
					default :
		
						sHeaderState = hsNone;
						break;

					case hsNone :

						sHeaderState = hsFirstCR;
						break;

					case hsFirstLF :

						sHeaderState = hsSecondCR;
						break;
				}

				break;

			case '\n' :

				switch (sHeaderState)
				{
					default :
		
						sHeaderState = hsNone;
						break;

					case hsFirstCR :

						sHeaderState = hsFirstLF;
						break;

					case hsSecondCR :

						sHeaderState = hsSecondLF;
						break;
				}

				break;
		}

		index += 1;
	}
}

static Handle <Value> writeASCII (const Arguments & arguments)
{
	try
	{
		uint32_t argumentCount (arguments.Length ( ));
	
		if (argumentCount)
		{
			uint32_t argumentIndex (0);
	
			do
			{
				String::AsciiValue ascii (arguments [argumentIndex]);
				char * asciiBytes (*ascii);
				int length (ascii.length ( ));
				if (!length) continue;

				if (sHeaderState < hsNoMoreHeaders)
				{
					MaintainHeaderState (asciiBytes, length);
				}

				if (1 != FCGI_fwrite (asciiBytes, length, 1, FCGI_stdout)) Throw (Exceptions::Errno ( ));
			}
			while (++argumentIndex < argumentCount);
		}
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> writeUTF8 (const Arguments & arguments)
{
	try
	{
		uint32_t argumentCount (arguments.Length ( ));
	
		if (argumentCount)
		{
			uint32_t argumentIndex (0);
	
			do
			{
				String::Utf8Value utf8 (arguments [argumentIndex]);
				char * utf8Bytes (*utf8);
				int length (utf8.length ( ));
				if (!length) continue;

				if (sHeaderState < hsNoMoreHeaders)
				{
					MaintainHeaderState (utf8Bytes, length);
				}

				if (1 != FCGI_fwrite (utf8Bytes, length, 1, FCGI_stdout)) Throw (Exceptions::Errno ( ));
			}
			while (++argumentIndex < argumentCount);
		}
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Handle <Value> headersComplete (Local <String> propertyName, const AccessorInfo & ai)
{
 	try
	{
		return Boolean::New (hsNoMoreHeaders <= sHeaderState);
	}
	catch (exception & e)
	{
		ScheduleJavaScriptException (&e);
	}
	catch (...)
	{
		ScheduleJavaScriptException (NULL);
	}

	return Undefined ( );
}

static Persistent <ObjectTemplate> & InstanceTemplate (void)
{
	static Persistent <ObjectTemplate> result;

	if (result.IsEmpty ( ))
	{
		result = Persistent <ObjectTemplate>::New (ObjectTemplate::New ( ));
		result->SetAccessor (String::New ("headersComplete"), headersComplete, NULL);
	}

	return result;
}

extern "C" Local <Value> instantiate (void)
{
	Local <Object> result (InstanceTemplate ( )->NewInstance ( ));

	Local <FunctionTemplate> templateWriteASCII (FunctionTemplate::New (writeASCII));
	result->Set (String::New ("writeASCII"), templateWriteASCII->GetFunction ( ));

	Local <FunctionTemplate> templateWriteUTF8 (FunctionTemplate::New (writeUTF8));
	result->Set (String::New ("writeUTF8"), templateWriteUTF8->GetFunction ( ));

	return result;
}
